Add TomTom .ov2 (POI) file support
authorparkrrrr <parkrrrr@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 28 Apr 2005 15:32:16 +0000 (15:32 +0000)
committerparkrrrr <parkrrrr@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Thu, 28 Apr 2005 15:32:16 +0000 (15:32 +0000)
gpsbabel/Makefile
gpsbabel/README
gpsbabel/reference/ov2-arc-out.ref [new file with mode: 0644]
gpsbabel/reference/ov2-geo-out.ref [new file with mode: 0644]
gpsbabel/reference/ov2-in.ref [new file with mode: 0644]
gpsbabel/testo
gpsbabel/tomtom.c [new file with mode: 0644]

index 600221e8a646ff2710951c0373a888aec24968ff..dbd766034790eab57ef51f1f5f967e06e46d3397 100644 (file)
@@ -27,7 +27,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \
        ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \
        igc.o brauniger_iq.o shape.o hiketech.o glogbook.o coastexp.o \
-       vcf.o overlay.o kml.o google.o lowranceusr.o an1.o
+       vcf.o overlay.o kml.o google.o lowranceusr.o an1.o tomtom.o
 
 FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o stackfilter.o
 
@@ -213,6 +213,7 @@ text.o: text.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \
   jeeps/gpsinput.h jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h
 tiger.o: tiger.c defs.h queue.h gbtypes.h csv_util.h
 tmpro.o: tmpro.c defs.h queue.h gbtypes.h csv_util.h
+tomtom.o: tomtom.c defs.h queue.h gbtypes.h
 tpg.o: tpg.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \
   jeeps/gpsport.h jeeps/gpsserial.h jeeps/gpssend.h jeeps/gpsread.h \
   jeeps/gpsutil.h jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h \
index 2af698072aca584332f096a20ef4177613c73d43..c2a873711fb6afd74b03bf368724f569d5c4f806 100644 (file)
@@ -784,10 +784,30 @@ THE FORMATS
 
         gpsbabel -i an1 -f one.an1 -f two.an1 -o an1 -F merged.an1
 
-        Currently, GPSBabel only writes drawing layers.  If your input
-        file is a road, track, trail, or waypoint layer, you should not
-        attempt to write to an .an1 file as the results may be 
-        unpredictable. 
+        In this case, the merged data will contain all of the properties
+        of the original data.
+
+        Currently, GPSBabel only writes drawing layers, as opposed to road, 
+        track, and other specialized layers.  If your input file is a road, 
+        track, trail, or waypoint layer, you should not attempt to write to 
+        an .an1 file as the results may be unpredictable.  Note that this 
+        also applies to merging files, so you can't currently merge two road
+        layers with GPSBabel (officially; there is an unsupported "type" 
+        option that works in limited cases.)
+
+    TomTom
+
+       This format can read and write TomTom .ov2 (POI) files, as used by the 
+        TomTom GO and TomTom Navigator.  It has been tested with an original
+        TomTom GO running version 5.00 of the TomTom software.  There may be
+        some records that confuse the input module - if you have an example
+        of such a record "in the wild", and you aren't restricted from sharing
+        it, we encourage you to post to the gpsbabel-misc mailing list to 
+        contact a developer.
+
+        Note that in addition to the .ov2 file, you will need a .bmp file for
+        the icon.  It should be 22x22 and 16 colors, and have the same name
+        (not including the extension) as the .ov2 file.
 
 DATA FILTERS
 
diff --git a/gpsbabel/reference/ov2-arc-out.ref b/gpsbabel/reference/ov2-arc-out.ref
new file mode 100644 (file)
index 0000000..f858319
Binary files /dev/null and b/gpsbabel/reference/ov2-arc-out.ref differ
diff --git a/gpsbabel/reference/ov2-geo-out.ref b/gpsbabel/reference/ov2-geo-out.ref
new file mode 100644 (file)
index 0000000..39c0f7c
Binary files /dev/null and b/gpsbabel/reference/ov2-geo-out.ref differ
diff --git a/gpsbabel/reference/ov2-in.ref b/gpsbabel/reference/ov2-in.ref
new file mode 100644 (file)
index 0000000..908e02e
--- /dev/null
@@ -0,0 +1,9 @@
+Mountain Bike Heaven by susy1313 3558.322N 08708.081W 0000000m Mountain Bike Heaven by susy13 a
+The Troll by a182pilot & Family 3605.441N 08640.773W 0000000m The Troll by a182pilot & Famil a
+Dive Bomber by JoGPS & family 3559.776N 08637.207W 0000000m Dive Bomber by JoGPS & family  a
+FOSTER by JoGPS & Family 3602.309N 08638.917W 0000000m FOSTER by JoGPS & Family       a
+Logan Lighthouse by JoGps & Family 3606.731N 08644.506W 0000000m Logan Lighthouse by JoGps & Fa a
+Ganier Cache by Susy1313 3603.845N 08647.431W 0000000m Ganier Cache by Susy1313       a
+Shy's Hill by FireFighterEng33 3605.266N 08648.584W 0000000m Shy's Hill by FireFighterEng33 a
+GittyUp by JoGPS / Warner Parks 3603.449N 08653.519W 0000000m GittyUp by JoGPS / Warner Park a
+Inlighting by JoGPS / Warner Parks 3604.967N 08652.037W 0000000m Inlighting by JoGPS / Warner P a
index 6c0df09a49579d8b62c8b58d7b741b3c139dac0e..d7541498d58d3f7fab55e1c3f1a6c7f484a1b7de 100755 (executable)
@@ -611,6 +611,22 @@ rm -f ${TMPDIR}/an1.out
 ${PNAME} -i google -f reference/google.js -o an1 -F ${TMPDIR}/an1.out
 compare ${TMPDIR}/an1.out reference/an1-line-out.ref
 
+#
+# TomTom .ov2 tests
+#
+
+rm -f ${TMPDIR}/ov2.out
+${PNAME} -i arc -f reference/google.arc -o tomtom -F ${TMPDIR}/ov2.out
+compare ${TMPDIR}/ov2.out reference/ov2-arc-out.ref
+
+rm -f ${TMPDIR}/ov2.out
+${PNAME} -i geo -f reference/gl.loc -o tomtom -F ${TMPDIR}/ov2.out
+compare ${TMPDIR}/ov2.out reference/ov2-geo-out.ref
+
+rm -f ${TMPDIR}/ov2.out
+${PNAME} -i tomtom -f reference/ov2-geo-out.ref -o gpsutil -F ${TMPDIR}/ov2.out
+compare ${TMPDIR}/ov2.out reference/ov2-in.ref
+
 #
 # XCSV "human readable" tests
 #
diff --git a/gpsbabel/tomtom.c b/gpsbabel/tomtom.c
new file mode 100644 (file)
index 0000000..abce6e5
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+    Read and write TomTom .ov2 files.
+
+    Copyright (C) 2005  Ronald Parker (babeltomtom@parkrrrr.com) and
+                        Robert Lipe (robertlipe@usa.net)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+
+/*
+    This module is based on my reverse-engineering of the .ov2 format, so
+    it might not be aware of all record types.  In particular, I've seen 
+    a type-3 record that may contain additional strings, but since I haven't
+    seen any of those from a legitimate source, I don't know what they are
+    supposed to contain.  Thus, they are not currently supported.  (The one
+    I saw was due to an errant pair of double-quotes in the input to
+    makeov2.exe.)   -- Ron Parker, 28 April 2005
+*/
+   
+
+#include "defs.h"
+
+#define MYNAME "TomTom"
+
+static FILE *file_in;
+static FILE *file_out;
+
+static
+arglist_t tomtom_args[] = {
+       {0, 0, 0, 0 }
+};
+
+static void
+rd_init(const char *fname)
+{
+       file_in = xfopen(fname, "rb", MYNAME);
+}
+
+static void
+rd_deinit(void)
+{
+       fclose(file_in);
+}
+
+static void
+wr_init(const char *fname)
+{
+       file_out = xfopen(fname, "wb", MYNAME);
+}
+
+static void
+wr_deinit(void)
+{
+       fclose(file_out);
+}
+
+static unsigned long
+read_long(FILE * f)
+{
+        gbuint32 result = 0;
+       
+        fread(&result, sizeof (result), 1, f);
+        return le_read32(&result);
+}
+
+static unsigned char
+read_char( FILE *f)
+{
+       unsigned char result = 0;
+       fread( &result, 1, 1, f );
+       return result;
+}
+
+static void
+data_read(void)
+{
+       unsigned char rectype;
+       long recsize;
+       long x;
+       long y;
+       char *desc;
+       waypoint *wpt_tmp;
+       while (!feof( file_in ) ) {
+               rectype = read_char( file_in );
+               if ( rectype == 1 ) {
+                       /* a block header; ignored on read */
+                       read_long( file_in );
+                       read_long( file_in );
+                       read_long( file_in );
+                       read_long( file_in );
+                       read_long( file_in );
+               }
+               else if ( rectype == 2 ) {
+                       recsize = read_long( file_in );
+                       x = read_long( file_in );
+                       y = read_long( file_in );
+                       desc = (char *)xmalloc( recsize - 13 );
+                       fread( desc, recsize-13, 1, file_in );
+                       
+                       wpt_tmp = waypt_new();
+
+                       wpt_tmp->longitude = x/100000.0;
+                       wpt_tmp->latitude = y/100000.0;
+                       wpt_tmp->description = desc;
+
+                       waypt_add(wpt_tmp);
+               }
+       }
+}
+
+
+struct hdr{
+       waypoint *wpt;
+};
+
+static
+int 
+compare_lat(const void *a, const void *b)
+{
+       const struct hdr *wa = a;
+       const struct hdr *wb = b;
+
+        double difference = wa->wpt->latitude - wb->wpt->latitude;
+       if ( difference < 0 ) {
+               return -1;
+       }
+       if ( difference ) {
+               return 1;
+       }
+       return 0;
+}
+
+static
+int 
+compare_lon(const void *a, const void *b)
+{
+       const struct hdr *wa = a;
+       const struct hdr *wb = b;
+
+        double difference = wa->wpt->longitude - wb->wpt->longitude;
+       if ( difference < 0 ) {
+               return -1;
+       }
+       if ( difference ) {
+               return 1;
+       }
+       return 0;
+}
+
+static void 
+write_long( FILE *file, long value ) {
+        gbuint32 tmp = 0;
+        le_write32( &tmp, value );
+                
+        fwrite( &tmp, sizeof(tmp), 1, file );
+} 
+
+static void
+write_char( FILE *file, unsigned char value ) {
+       fwrite( &value, 1, 1, file );
+}
+
+static void
+write_string( FILE *file, char *str ) {
+       fwrite( str, strlen(str), 1, file );
+       write_char( file, '\0' );
+}
+
+struct blockheader {
+       struct hdr *start;
+       long count;
+       long size;
+       double minlat;
+       double maxlat;
+       double minlon; 
+       double maxlon;
+       struct blockheader *ch1;
+       struct blockheader *ch2;
+};
+
+static void
+write_blocks( FILE *f, struct blockheader *blocks ) {
+       int i;
+       write_char( f, 1 );
+       write_long( f, blocks->size );
+       write_long( f, blocks->maxlon*100000 );
+       write_long( f, blocks->maxlat*100000 );
+       write_long( f, blocks->minlon*100000 );
+       write_long( f, blocks->minlat*100000 );
+       if ( blocks->ch1 ) {
+               write_blocks( f, blocks->ch1 );
+       }
+       if ( blocks->ch2 ) {
+               write_blocks( f, blocks->ch2 );
+       }
+       if ( !blocks->ch1 && !blocks->ch2 ) {
+               for ( i = 0; i < blocks->count; i++ ) {
+                       write_char( f, 2 );
+                       write_long( f, 
+                               strlen( blocks->start[i].wpt->description ) 
+                               + 14 );
+                       write_long( f, blocks->start[i].wpt->longitude*100000);
+                       write_long( f, blocks->start[i].wpt->latitude*100000);
+                       write_string( f, blocks->start[i].wpt->description );
+               }
+       }
+}      
+
+static struct blockheader *
+compute_blocks( struct hdr *start, int count,
+            double minlon, double maxlon, double minlat, double maxlat ) {
+       struct blockheader *newblock;
+       
+       newblock = (struct blockheader *)xcalloc( sizeof( *newblock ), 1);
+       newblock->start = start;
+       newblock->count = count;
+       newblock->minlon = minlon;
+       newblock->maxlon = maxlon;
+       newblock->minlat = minlat;
+       newblock->maxlat = maxlat;
+       newblock->size = 4 * 5 + 1;   /* hdr is 5 longs, 1 char */
+       if ( count < 20 ) {
+               int i;
+               waypoint *wpt = NULL;
+
+               for ( i = 0; i < count; i++ ) {
+                       newblock->size += 4 * 3 + 1;  
+                               /* wpt const part 3 longs, 1 char */
+                       wpt = start[i].wpt;
+                       newblock->size += strlen( wpt->description ) + 1;
+               }       
+       }
+       else {
+               if ( (maxlat-minlat)>(maxlon-minlon)) {
+                       /* split along lats */
+                       qsort( start, count, sizeof(*start), compare_lat);
+                       newblock->ch1 = compute_blocks( start, count/2,
+                               minlon, maxlon, minlat, 
+                               start[count/2].wpt->latitude );
+                       newblock->ch2 = compute_blocks( start+count/2, 
+                               count-count/2, minlon, maxlon,
+                               start[count/2].wpt->latitude, maxlat );
+               }       
+               else {
+                       /* split along lons */
+                       qsort( start, count, sizeof(*start), compare_lon);
+                       newblock->ch1 = compute_blocks( start, count/2,
+                               minlon, start[count/2].wpt->longitude, 
+                               minlat, maxlat );
+                       newblock->ch2 = compute_blocks( start+count/2, 
+                               count-count/2, start[count/2].wpt->longitude, 
+                               maxlon, minlat, maxlat );
+               }
+               if ( newblock->ch1 ) {
+                       newblock->size += newblock->ch1->size;
+               }
+               if ( newblock->ch2 ) {
+                       newblock->size += newblock->ch2->size;
+               }
+       }
+       return newblock;
+}
+
+static void
+free_blocks( struct blockheader *block ) {
+       if ( block->ch1 ) free_blocks( block->ch1 );
+       if ( block->ch2 ) free_blocks( block->ch2 );
+       xfree( block );
+}
+
+static void
+data_write(void)
+{
+       int ct = waypt_count();
+       struct hdr *htable, *bh;
+        queue *elem, *tmp;
+       extern queue waypt_head;
+        waypoint *waypointp;
+       double minlon = 200;
+       double maxlon = -200;
+       double minlat = 200;
+       double maxlat = -200;
+       struct blockheader *blocks = NULL;
+
+       htable = xmalloc(ct * sizeof(*htable));
+       bh = htable;
+
+        QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
+           waypointp = (waypoint *) elem;
+           bh->wpt = waypointp;
+           if ( waypointp->longitude > maxlon ) {
+                   maxlon = waypointp->longitude;
+           }
+           if ( waypointp->longitude < minlon ) {
+                   minlon = waypointp->longitude;
+           }
+           if ( waypointp->latitude > maxlat ) {
+                   maxlat = waypointp->latitude;
+           }
+           if ( waypointp->latitude < minlat ) {
+                   minlat = waypointp->latitude;
+           }
+           bh ++;
+       }
+
+       blocks = compute_blocks( htable, ct, minlon, maxlon, minlat, maxlat );
+       write_blocks( file_out, blocks );
+       free_blocks( blocks );
+       
+       xfree(htable);
+}
+
+ff_vecs_t tomtom_vecs = {
+       ff_type_file,
+       FF_CAP_RW_WPT,
+       rd_init,
+       wr_init,
+       rd_deinit,
+       wr_deinit,
+       data_read,
+       data_write,
+       NULL,
+       tomtom_args,
+};